package Storage;

import Object.DSException;
import Object.DSHistory;
import Object.DSQuery;
import Task.DSTask;
import Task.DSTaskList;

//@author A0103535R
/************************************ Abstract Class DSStorage ***************************/
public class DSStorage {
        private static DSQuery _currentQuery = null;
        private static DSTaskStore _taskStore = null;
        private static DSHistoryStore _historyStore = null;
        private static DSSettingStore _settingStore = null;
        
    	/**
    	 * This is the constructor of DSStorage.
    	 * 
    	 * @throws DSException
    	 */
        public DSStorage() throws DSException {
                if (_taskStore == null) {
                        _taskStore = DSTaskStore.getInstance();
                }
                
                if (_historyStore == null){
                        _historyStore = DSHistoryStore.getInstance();
                }
                
                if (_settingStore == null){
                		_settingStore = DSSettingStore.getInstance();
                }
                
                if (_currentQuery == null){
                        _currentQuery = new DSQuery();
                        _currentQuery.setDoneStatus(DSQuery.STATUS_BOTH);
                }
                        
                assert _taskStore != null;
                assert _historyStore != null;
                assert _settingStore != null;
                assert _currentQuery != null;
        }

    	/**
    	 * This function is used to add new task into Storage
    	 * 
    	 * @param task
    	 * 			task to add in storage.
    	 * 
    	 * @return the result of process.
    	 * 
    	 * @throws DSException
    	 */
        public boolean add(DSTask task) throws DSException {
                if(_taskStore.add(task)){
                        _historyStore.addHistory(DSHistory.DSHISTORY_ADD_TASK, task);
                        return true;
                }
                return false;   
        }

    	/**
    	 * This function is used to remove task with specific id from storage.
    	 * 
    	 * @param taskId
    	 * 			the taskId of task to remove from storage.
    	 * 
    	 * @return result of process.
    	 * 
    	 * @throws DSException
    	 */
        public boolean remove(String taskId) throws DSException {
                DSTask oldTask = _taskStore.remove(taskId);
                if(oldTask != null){
                        _historyStore.addHistory(DSHistory.DSHISTORY_DELETE_TASK, oldTask);
                        return true;
                }
                return false;
        }

    	/**
    	 * This function is used to update specific task in storage.
    	 * 
    	 * @param newTask
    	 * 			task to update in storage.
    	 * 
    	 * @return result of process.
    	 * 
    	 * @throws DSException
    	 */
        public boolean update(DSTask newTask) throws DSException {
                DSTask oldTask = _taskStore.update(newTask);
                if(oldTask != null){
                        _historyStore.addHistory(DSHistory.DSHISTORY_UPDATE_TASK, oldTask);
                        return true;
                }
                return false;
        }

    	/**
    	 * This function is used to search tasks in storage.
    	 * 
    	 * @param query
    	 * 			requirements for searching.
    	 * 
    	 * @return task list of tasks meeting the requirements.
    	 * 
    	 * @throws DSException
    	 */
        public DSTaskList search(DSQuery query) throws DSException{
                DSTaskList list = _taskStore.search(query);
                if(list != null){
                        _historyStore.addHistory(DSHistory.DSHISTORY_QUERY, _currentQuery);
                        _currentQuery = query;
                        return list;
                }
                return null;
        }
        
    	/**
    	 * This function is used to undo previous operation.
    	 * 
    	 * @return task list after the operation of undo
    	 * 
    	 * @throws DSException
    	 */
        public DSTaskList undo() throws DSException{
                DSHistory history = _historyStore.getLastHistory();
                if(history != null){
                        return executeUndo(history);
                }else{
                        return search(_currentQuery);
                }
        }
        
    	/**
    	 * This function is used to execute undo operation.
    	 * 
    	 * @param history
    	 * 			the previous operation to undo.
    	 * 
    	 * @return task list after undo operation.
    	 * 
    	 * @throws DSException
    	 */
        private DSTaskList executeUndo(DSHistory history) throws DSException{
                switch (history.getType()) {
                case DSHistory.DSHISTORY_ADD_TASK:
                        return undoAdd(history.getTask());
                case DSHistory.DSHISTORY_DELETE_TASK:
                        return undoDelete(history.getTask());
                case DSHistory.DSHISTORY_UPDATE_TASK:
                        return undoUpdate(history.getTask());     
                case DSHistory.DSHISTORY_QUERY:
                        return undoQuery(history.getQuery());
                default:
                        break;
                }
                return null;
        }
        
    	/**
    	 * This function is used to undo add operation.
    	 * 
    	 * @param task
    	 * 			task to remove from storage.
    	 * 
    	 * @return the task list after processing the undo of add operation.
    	 * 
    	 * @throws DSException
    	 */
        private DSTaskList undoAdd(DSTask task) throws DSException{
                _taskStore.remove(task.getTaskId());
                return _taskStore.search(_currentQuery);
        }
        
    	/**
    	 * This function is used to undo delete operation.
    	 * 
    	 * @param task
    	 * 			task to add in storage.
    	 * 
    	 * @return the task list after processing the undo of delete operation.
    	 * 
    	 * @throws DSException
    	 */
        private DSTaskList undoDelete(DSTask task) throws DSException{
                _taskStore.add(task);
                return _taskStore.search(_currentQuery);
        }
        
    	/**
    	 * This function is used to undo update operation.
    	 * 
    	 * @param task
    	 * 			task to undo update in storage.
    	 * 
    	 * @return task list after processing the undo of delete operation.
    	 * 
    	 * @throws DSException
    	 */
        private DSTaskList undoUpdate(DSTask task) throws DSException{
                _taskStore.update(task);
                return _taskStore.search(_currentQuery);
        }
        
    	/**
    	 * This function is used to undo search operation
    	 * 
    	 * @param query
    	 * 			previous query before current one.
    	 * 
    	 * @return the singleton instance of DSFileHandler
    	 */
        private DSTaskList undoQuery(DSQuery query){
                _currentQuery = query;
                return _taskStore.search(query);
        }
        
    	/**
    	 * This function is used to get all tasks from storage.
    	 * 
    	 * @return task list of all tasks in storage.
    	 * 
    	 * @throws DSException
    	 */
        public DSTaskList getTaskList() throws DSException {
                DSTaskList list = _taskStore.getTaskList();
                return list;
        }

    	/**
    	 * This function is used to clear all tasks in storage.
    	 */
        public void clearAll() throws DSException{
                _taskStore.clearAll();
        }
        
    	/**
    	 * This function is used to update the background setting to storage.
    	 * 
    	 * @throws DSException
    	 */
        public void updateBackgroundNum(int backgroundNum) throws DSException{
        	_settingStore.updateBackgroundNum(backgroundNum);
        }
        
    	/**
    	 * This function is used to update the animation setting to storage.
    	 * 
    	 * @throws DSException
    	 */
        public void updateAnimation(boolean isAnimated) throws DSException{
        	_settingStore.updateAnimation(isAnimated);
        }
        
    	/**
    	 * This function is used to get the background setting from storage.
    	 * 
    	 * @throws DSException
    	 */
        public int getBackgroundNum() throws DSException{
        	return _settingStore.getBackgroundNum();
        }
        
    	/**
    	 * This function is used to get the animation setting from storage.
    	 * 
    	 * @throws DSException
    	 */
        public boolean isAnimated () throws DSException{
        	return _settingStore.isAinimated();
        }
}